package lion.framework.model.gen;

import java.io.File;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.WordUtils;

import lion.dev.fun.Funs;
import lion.dev.lang.MapJ;

/**
 *
 *
 *
 * @author lion
 * @email hl_0074@sina.com
 * @date 2014年4月4日
 */
public class Model {

	public static void pojo(String clazz, String[][] fields) {
		pojo("src", clazz, fields);
	}

	/**
	 * POJO
	 */
	public static void pojo(String base, String clazz, String[][] fields) {

		String template;
		try {
			template = IOUtils.toString(Model.class.getResourceAsStream("tmpl/pojo.tmpl"));
			define(base, template, clazz, fields, null);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void rbm(String clazz, String[][] fields) {
		rbm("src", clazz, fields);
	}

	/**
	 * Rich Blood Mode
	 */
	public static void rbm(String base, String clazz, String[][] fields) {

		// check 主键
		if (!checkPrimaryKey(fields)) { return; }

		Funs fun = new Funs();
		fun.add("insertsql", (Object... args) -> {

			MapJ context = (MapJ) args[0];
			String[][] sfields = context.getE("fields");
			StringBuilder sbu = new StringBuilder("insert into  \" + TABLE_NAME +\"(");
			StringBuilder sbu1 = new StringBuilder();
			StringBuilder sbu2 = new StringBuilder();
			boolean b = false;
			for (String[] f : sfields) {
				if (f[0].equalsIgnoreCase("id")) {
					continue;
				}
				if (b) {
					sbu1.append(",");
					sbu2.append(",");
				}
				b = true;
				sbu1.append(f[0]);
				sbu2.append(":" + f[0]);
			}
			sbu.append(sbu1.toString() + ") values (" + sbu2.toString() + ")");
			return sbu.toString();
		});

		fun.add("updatesql", (Object... args) -> {

			MapJ context = (MapJ) args[0];
			String[][] sfields = context.getE("fields");
			StringBuilder sbu = new StringBuilder("update \" + TABLE_NAME + \" set ");
			boolean b = false;
			for (String[] f : sfields) {
				if (f[0].equalsIgnoreCase("id")) {
					continue;
				}
				if (b) {
					sbu.append(",");
				}
				b = true;
				sbu.append(f[0] + "=:" + f[0]);
			}
			sbu.append(" where id=:id");
			return sbu.toString();
		});

		// funs
		try {
			String template = IOUtils.toString(Model.class.getResourceAsStream("tmpl/rbm.tmpl"));
			define(base, template, clazz, fields, fun);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private static boolean checkPrimaryKey(String[][] fields) {

		for (String[] field : fields) {
			if (StringUtils.equals(field[0], "id")) { return true; }
		}

		return false;
	}

	public static void define(String template, String clazz, String[][] fields) {

		define("src", template, clazz, fields, null);
	}

	public static void define(String base, String template, String clazz, String[][] fields, Funs funs) {

		if (StringUtils.isBlank(template)) {
			try {
				template = IOUtils.toString(Model.class.getResourceAsStream("pojo.tmpl"));
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

		String className = WordUtils.capitalize(clazz.replaceAll(".*\\.", ""));
		String packageName = clazz.replaceAll("\\.\\w+$", "");

		Pattern pattern = Pattern.compile("(?s)\\$\\{foreach fields\\}(.*?)\\$\\{endfor\\}");
		Matcher matcher = pattern.matcher(template);
		StringBuffer sbu = new StringBuffer();
		while (matcher.find()) {
			matcher.appendReplacement(sbu, doLoop(matcher.group(1).replaceAll("\\s+$", ""), fields));
		}
		matcher.appendTail(sbu);
		String result = StringUtils.replaceEach(sbu.toString(),
				new String[] { "${class}", "${package}", "${fields}", "${getset}", "${lower:class}" },
				new String[] { className, packageName, fields(fields), getsets(fields), className.toLowerCase() });

		if (funs != null) {
			MapJ context = new MapJ();
			context.put("class", className);
			context.put("package", packageName);
			for (String key : funs.funs().keySet()) {
				context.put("fields", fields);
				try {
					result = result.replace("${" + key + "}",
							StringUtils.trimToEmpty(((String) funs.exec(key, context))));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}

		String folder = System.getProperty("user.dir") + "/" + base + "/" + packageName.replaceAll("\\.", "/") + "/";
		new File(folder).mkdirs();

		String fileName = folder + className + ".java";

		try {
			FileUtils.writeStringToFile(new File(fileName), result);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private static String doLoop(String content, String[][] fields) {

		StringBuilder buffer = new StringBuilder();

		for (String[] field : fields) {
			buffer.append(content.replace("${name}", field[0]).replace("${type}", field[1]));
		}
		return buffer.toString();
	}

	private static String getsets(String[][] fields) {

		String setter = "\n\tpublic void {0}({1} {2})'{'this.{2}={2};'}'";
		String getter = "\n\tpublic {0} {1}()'{'return this.{2};'}'";

		StringBuilder fieldBuffer = new StringBuilder();
		for (String[] field : fields) {

			String gfn = "";
			String sfn = "";

			if (StringUtils.equalsIgnoreCase(field[1], "boolean")) {
				String fld = field[0];
				if (StringUtils.startsWithIgnoreCase(field[0], "is")) {
					fld = field[0].substring(2);
				}
				sfn = "set" + WordUtils.capitalize(fld);
				gfn = "is" + WordUtils.capitalize(fld);
			} else {
				sfn = "set" + WordUtils.capitalize(field[0]);
				gfn = "get" + WordUtils.capitalize(field[0]);
			}

			fieldBuffer.append(MessageFormat.format(setter, sfn, field[1], field[0]));
			fieldBuffer.append(MessageFormat.format(getter, field[1], gfn, field[0]));
		}
		return fieldBuffer.toString();
	}

	private static String fields(String[][] fields) {

		StringBuilder fieldBuffer = new StringBuilder();
		for (String[] field : fields) {

			fieldBuffer.append("\n\tprivate " + field[1] + " " + field[0]);
			if (field.length == 3) {
				fieldBuffer.append(" = " + field[2]);
			}
			fieldBuffer.append(";");
		}
		return fieldBuffer.toString();
	}

}
